home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume15 / stevie / part04 < prev    next >
Encoding:
Internet Message Format  |  1988-06-05  |  25.9 KB

  1. Subject:  v15i040:  Stevie, an "aspiring" VI clone for Unix, OS/2, Amiga, Part04/04
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: onecom!wldrdg!tony (Tony Andrews)
  7. Posting-number: Volume 15, Issue 40
  8. Archive-name: stevie/part04
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 4 (of 4)."
  17. # Contents:  normal.c
  18. # Wrapped by rsalz@fig.bbn.com on Sun Jun  5 11:45:50 1988
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f 'normal.c' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'normal.c'\"
  22. else
  23. echo shar: Extracting \"'normal.c'\" \(23691 characters\)
  24. sed "s/^X//" >'normal.c' <<'END_OF_FILE'
  25. X/*
  26. X * STevie - ST editor for VI enthusiasts.    ...Tim Thompson...twitch!tjt...
  27. X *
  28. X * Extensive modifications by:  Tony Andrews       onecom!wldrdg!tony
  29. X *
  30. X */
  31. X
  32. X/*
  33. X * This file contains the main routine for processing characters in
  34. X * command mode as well as routines for handling the operators.
  35. X */
  36. X
  37. X#include "stevie.h"
  38. X
  39. static    void    doshift(), dodelete(), doput(), dochange();
  40. static    void    tabinout(), startinsert();
  41. static    bool_t    dojoin(), doyank();
  42. X
  43. X/*
  44. X * Macro evaluates true if char 'c' is a valid identifier character
  45. X */
  46. X#define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  47. X
  48. X/*
  49. X * 'can_undo' is a relatively temporary hack so I can debug the 'undo'
  50. X * code for various operations independently. If 'can_undo' is set,
  51. X * then the most recent edit can be undone. Otherwise, attempting to
  52. X * undo an edit will result in an apologetic message. Can_undo is
  53. X * cleared in the macro 'CHANGED', so that every change, by default,
  54. X * cannot be undone. If the undo code for an edit works, 'can_undo'
  55. X * should be set, AFTER the CHANGED macro is invoked.
  56. X */
  57. bool_t    can_undo = FALSE;
  58. X
  59. X/*
  60. X * Operators
  61. X */
  62. X#define    NOP    0        /* no pending operation */
  63. X#define    DELETE    1
  64. X#define    YANK    2
  65. X#define    CHANGE    3
  66. X#define    LSHIFT    4
  67. X#define    RSHIFT    5
  68. X
  69. X#define    CLEAROP    (operator = NOP)    /* clear any pending operator */
  70. X
  71. static    int    operator = NOP;        /* current pending operator */
  72. X
  73. X/*
  74. X * When a cursor motion command is made, it is marked as being a character
  75. X * or line oriented motion. Then, if an operator is in effect, the operation
  76. X * becomes character or line oriented accordingly.
  77. X *
  78. X * Character motions are marked as being inclusive or not. Most char.
  79. X * motions are inclusive, but some (e.g. 'w') are not.
  80. X *
  81. X * Generally speaking, every command in normal() should either clear any
  82. X * pending operator (with CLEAROP), or set the motion type variable.
  83. X */
  84. X
  85. X/*
  86. X * Motion types
  87. X */
  88. X#define    MBAD    (-1)        /* 'bad' motion type marks unusable yank buf */
  89. X#define    MCHAR    0
  90. X#define    MLINE    1
  91. X
  92. static    int    mtype;            /* type of the current cursor motion */
  93. static    bool_t    mincl;            /* true if char motion is inclusive */
  94. X
  95. static    LPTR    startop;        /* cursor pos. at start of operator */
  96. X
  97. X/*
  98. X * Operators can have counts either before the operator, or between the
  99. X * operator and the following cursor motion as in:
  100. X *
  101. X *    d3w or 3dw
  102. X *
  103. X * If a count is given before the operator, it is saved in opnum. If
  104. X * normal() is called with a pending operator, the count in opnum (if
  105. X * present) overrides any count that came later.
  106. X */
  107. static    int    opnum = 0;
  108. X
  109. X
  110. X#define    DEFAULT1(x)    (((x) == 0) ? 1 : (x))
  111. X
  112. X/*
  113. X * normal
  114. X *
  115. X * Execute a command in normal mode.
  116. X */
  117. X
  118. void
  119. normal(c)
  120. int c;
  121. X{
  122. X    char *p, *q;
  123. X    int n;
  124. X    bool_t flag = FALSE;
  125. X    int type = 0;        /* used in some operations to modify type */
  126. X    int dir = FORWARD;    /* search direction */
  127. X    int nchar = NUL;
  128. X    bool_t finish_op;
  129. X
  130. X    /*
  131. X     * If there is an operator pending, then the command we take
  132. X     * this time will terminate it. Finish_op tells us to finish
  133. X     * the operation before returning this time (unless the operation
  134. X     * was cancelled.
  135. X     */
  136. X    finish_op = (operator != NOP);
  137. X
  138. X    /*
  139. X     * If we're in the middle of an operator AND we had a count before
  140. X     * the operator, then that count overrides the current value of
  141. X     * Prenum. What this means effectively, is that commands like
  142. X     * "3dw" get turned into "d3w" which makes things fall into place
  143. X     * pretty neatly.
  144. X     */
  145. X    if (finish_op) {
  146. X        if (opnum != 0)
  147. X            Prenum = opnum;
  148. X    } else
  149. X        opnum = 0;
  150. X
  151. X    switch(c & 0xff){
  152. X
  153. X    case K_HELP:
  154. X        CLEAROP;
  155. X        if (help()) {
  156. X            screenclear();
  157. X            updatescreen();
  158. X        }
  159. X        break;
  160. X
  161. X    case CTRL('L'):
  162. X        CLEAROP;
  163. X        screenclear();
  164. X        updatescreen();
  165. X        break;
  166. X
  167. X    case CTRL('D'):
  168. X        CLEAROP;
  169. X        if (Prenum)
  170. X            P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  171. X        scrollup(P(P_SS));
  172. X        onedown(P(P_SS));
  173. X        updatescreen();
  174. X        break;
  175. X
  176. X    case CTRL('U'):
  177. X        CLEAROP;
  178. X        if (Prenum)
  179. X            P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  180. X        scrolldown(P(P_SS));
  181. X        oneup(P(P_SS));
  182. X        updatescreen();
  183. X        break;
  184. X
  185. X    /*
  186. X     * ^F and ^B are neat hacks, but don't take counts. This is very
  187. X     * code-efficient, and does the right thing. I'll fix it later
  188. X     * to take a count. The old code took a count, but didn't do the
  189. X     * right thing in other respects (e.g. leaving some context).
  190. X     */
  191. X    case CTRL('F'):
  192. X#if 1
  193. X        screenclear();
  194. X        stuffin("Lz\nM");
  195. X#else
  196. X        /*
  197. X         * Old code
  198. X         */
  199. X        CLEAROP;
  200. X        n = DEFAULT1(Prenum);
  201. X        if ( ! onedown(Rows * n) )
  202. X            beep();
  203. X        cursupdate();
  204. X#endif
  205. X        break;
  206. X
  207. X    case CTRL('B'):
  208. X#if 1
  209. X        screenclear();
  210. X        stuffin("Hz-M");
  211. X#else
  212. X        /*
  213. X         * Old code
  214. X         */
  215. X        CLEAROP;
  216. X        n = DEFAULT1(Prenum);
  217. X        if ( ! oneup(Rows * n) )
  218. X            beep();
  219. X        cursupdate();
  220. X#endif
  221. X        break;
  222. X
  223. X    case CTRL('E'):
  224. X        CLEAROP;
  225. X        scrollup(DEFAULT1(Prenum));
  226. X        updatescreen();
  227. X        break;
  228. X
  229. X    case CTRL('Y'):
  230. X        CLEAROP;
  231. X        scrolldown(DEFAULT1(Prenum));
  232. X        updatescreen();
  233. X        break;
  234. X
  235. X    case 'z':
  236. X        CLEAROP;
  237. X        switch (vgetc()) {
  238. X        case NL:        /* put Curschar at top of screen */
  239. X        case CR:
  240. X            *Topchar = *Curschar;
  241. X            Topchar->index = 0;
  242. X            updatescreen();
  243. X            break;
  244. X
  245. X        case '.':        /* put Curschar in middle of screen */
  246. X            n = Rows/2;
  247. X            goto dozcmd;
  248. X
  249. X        case '-':        /* put Curschar at bottom of screen */
  250. X            n = Rows-1;
  251. X            /* fall through */
  252. X
  253. X        dozcmd:
  254. X            {
  255. X                register LPTR    *lp = Curschar;
  256. X                register int    l = 0;
  257. X
  258. X                while ((l < n) && (lp != NULL)) {
  259. X                    l += plines(lp);
  260. X                    *Topchar = *lp;
  261. X                    lp = prevline(lp);
  262. X                }
  263. X            }
  264. X            Topchar->index = 0;
  265. X            updatescreen();
  266. X            break;
  267. X
  268. X        default:
  269. X            beep();
  270. X        }
  271. X        break;
  272. X
  273. X    case CTRL('G'):
  274. X        CLEAROP;
  275. X        fileinfo();
  276. X        break;
  277. X
  278. X    case 'G':
  279. X        mtype = MLINE;
  280. X        *Curschar = *gotoline(Prenum);
  281. X        break;
  282. X
  283. X    case 'H':
  284. X        mtype = MLINE;
  285. X        *Curschar = *Topchar;
  286. X        for (n = Prenum; n && onedown(1) ;n--)
  287. X            ;
  288. X        beginline(TRUE);
  289. X        break;
  290. X
  291. X    case 'M':
  292. X        mtype = MLINE;
  293. X        *Curschar = *Topchar;
  294. X        for (n = 0; n < Rows/2 && onedown(1) ;n++)
  295. X            ;
  296. X        beginline(TRUE);
  297. X        break;
  298. X
  299. X    case 'L':
  300. X        mtype = MLINE;
  301. X        *Curschar = *prevline(Botchar);
  302. X        for (n = Prenum; n && oneup(1) ;n--)
  303. X            ;
  304. X        beginline(TRUE);
  305. X        break;
  306. X
  307. X    case 'l':
  308. X    case K_RARROW:
  309. X    case ' ':
  310. X        mtype = MCHAR;
  311. X        mincl = FALSE;
  312. X        n = DEFAULT1(Prenum);
  313. X        while (n--) {
  314. X            if ( ! oneright() )
  315. X                beep();
  316. X        }
  317. X        set_want_col = TRUE;
  318. X        break;
  319. X
  320. X    case 'h':
  321. X    case K_LARROW:
  322. X    case CTRL('H'):
  323. X        mtype = MCHAR;
  324. X        mincl = FALSE;
  325. X        n = DEFAULT1(Prenum);
  326. X        while (n--) {
  327. X            if ( ! oneleft() )
  328. X                beep();
  329. X        }
  330. X        set_want_col = TRUE;
  331. X        break;
  332. X
  333. X    case '-':
  334. X        flag = TRUE;
  335. X        /* fall through */
  336. X
  337. X    case 'k':
  338. X    case K_UARROW:
  339. X    case CTRL('P'):
  340. X        mtype = MLINE;
  341. X        if ( ! oneup(DEFAULT1(Prenum)) )
  342. X            beep();
  343. X        if (flag)
  344. X            beginline(TRUE);
  345. X        break;
  346. X
  347. X    case '+':
  348. X    case CR:
  349. X    case NL:
  350. X        flag = TRUE;
  351. X        /* fall through */
  352. X
  353. X    case 'j':
  354. X    case K_DARROW:
  355. X    case CTRL('N'):
  356. X        mtype = MLINE;
  357. X        if ( ! onedown(DEFAULT1(Prenum)) )
  358. X            beep();
  359. X        if (flag)
  360. X            beginline(TRUE);
  361. X        break;
  362. X
  363. X    /*
  364. X     * This is a strange motion command that helps make operators
  365. X     * more logical. It is actually implemented, but not documented
  366. X     * in the real 'vi'. This motion command actually refers to "the
  367. X     * current line". Commands like "dd" and "yy" are really an alternate
  368. X     * form of "d_" and "y_". It does accept a count, so "d3_" works to
  369. X     * delete 3 lines.
  370. X     */
  371. X    case '_':
  372. X    lineop:
  373. X        mtype = MLINE;
  374. X        onedown(DEFAULT1(Prenum)-1);
  375. X        break;
  376. X
  377. X    case '|':
  378. X        mtype = MCHAR;
  379. X        mincl = TRUE;
  380. X        beginline(FALSE);
  381. X        if (Prenum > 0)
  382. X            *Curschar = *coladvance(Curschar, Prenum-1);
  383. X        Curswant = Prenum - 1;
  384. X        break;
  385. X        
  386. X    case CTRL(']'):            /* :ta to current identifier */
  387. X        CLEAROP;
  388. X        {
  389. X            char    c;
  390. X            LPTR    save;
  391. X
  392. X            save = *Curschar;
  393. X            /*
  394. X             * First back up to start of identifier. This
  395. X             * doesn't match the real vi but I like it a
  396. X             * little better and it shouldn't bother anyone.
  397. X             */
  398. X            c = gchar(Curschar);
  399. X            while (IDCHAR(c)) {
  400. X                if (!oneleft())
  401. X                    break;
  402. X                c = gchar(Curschar);
  403. X            }
  404. X            if (!IDCHAR(c))
  405. X                oneright();
  406. X
  407. X            stuffin(":ta ");
  408. X            /*
  409. X             * Now grab the chars in the identifier
  410. X             */
  411. X            c = gchar(Curschar);
  412. X            while (IDCHAR(c)) {
  413. X                stuffin(mkstr(c));
  414. X                if (!oneright())
  415. X                    break;
  416. X                c = gchar(Curschar);
  417. X            }
  418. X            stuffin("\n");
  419. X
  420. X            *Curschar = save;    /* restore, in case of error */
  421. X        }
  422. X        break;
  423. X
  424. X    case '%':
  425. X        mtype = MCHAR;
  426. X        mincl = TRUE;
  427. X        {
  428. X            LPTR    *pos;
  429. X
  430. X            if ((pos = showmatch()) == NULL)
  431. X                beep();
  432. X            else {
  433. X                setpcmark();
  434. X                *Curschar = *pos;
  435. X                set_want_col = TRUE;
  436. X            }
  437. X        }
  438. X        break;
  439. X        
  440. X    /*
  441. X     * Word Motions
  442. X     */
  443. X
  444. X    case 'B':
  445. X        type = 1;
  446. X        /* fall through */
  447. X
  448. X    case 'b':
  449. X        mtype = MCHAR;
  450. X        mincl = FALSE;
  451. X        set_want_col = TRUE;
  452. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  453. X            LPTR    *pos;
  454. X
  455. X            if ((pos = bck_word(Curschar, type)) == NULL) {
  456. X                beep();
  457. X                break;
  458. X            } else
  459. X                *Curschar = *pos;
  460. X        }
  461. X        break;
  462. X
  463. X    case 'W':
  464. X        type = 1;
  465. X        /* fall through */
  466. X
  467. X    case 'w':
  468. X        /*
  469. X         * This is a little strange. To match what the real vi
  470. X         * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
  471. X         * This seems impolite at first, but it's really more
  472. X         * what we mean when we say 'cw'.
  473. X         */
  474. X        if (operator == CHANGE)
  475. X            goto doecmd;
  476. X
  477. X        mtype = MCHAR;
  478. X        mincl = FALSE;
  479. X        set_want_col = TRUE;
  480. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  481. X            LPTR    *pos;
  482. X
  483. X            if ((pos = fwd_word(Curschar, type)) == NULL) {
  484. X                beep();
  485. X                break;
  486. X            } else
  487. X                *Curschar = *pos;
  488. X        }
  489. X        break;
  490. X
  491. X    case 'E':
  492. X        type = 1;
  493. X        /* fall through */
  494. X
  495. X    case 'e':
  496. X    doecmd:
  497. X        mtype = MCHAR;
  498. X        mincl = TRUE;
  499. X        set_want_col = TRUE;
  500. X        for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  501. X            LPTR    *pos;
  502. X
  503. X            if ((pos = end_word(Curschar, type)) == NULL) {
  504. X                beep();
  505. X                break;
  506. X            } else
  507. X                *Curschar = *pos;
  508. X        }
  509. X        break;
  510. X
  511. X    case '$':
  512. X        mtype = MCHAR;
  513. X        mincl = TRUE;
  514. X        while ( oneright() )
  515. X            ;
  516. X        Curswant = 999;        /* so we stay at the end */
  517. X        break;
  518. X
  519. X    case '^':
  520. X        flag = TRUE;
  521. X        /* fall through */
  522. X
  523. X    case '0':
  524. X        mtype = MCHAR;
  525. X        mincl = TRUE;
  526. X        beginline(flag);
  527. X        break;
  528. X
  529. X    case 'x':
  530. X        CLEAROP;
  531. X        if (lineempty())    /* can't do it on a blank line */
  532. X            beep();
  533. X        if (Prenum)
  534. X            stuffnum(Prenum);
  535. X        stuffin("d.");
  536. X        break;
  537. X
  538. X#if 0
  539. X        /* Can't do it if we're on a blank line. */
  540. X        if (lineempty())
  541. X            beep();
  542. X        else {
  543. X            addtobuff(Redobuff,'x',NULL);
  544. X            /* To undo it, we insert the same character back. */
  545. X            resetundo();
  546. X            addtobuff(Undobuff, 'i', gchar(Curschar), ESC, NUL);
  547. X            *Uncurschar = *Curschar;
  548. X            delchar(TRUE);
  549. X            updateline();
  550. X        }
  551. X        break;
  552. X#endif
  553. X
  554. X    case 'X':
  555. X        CLEAROP;
  556. X        if (!oneleft())
  557. X            beep();
  558. X        else {
  559. X            addtobuff(Redobuff, 'X', NUL);
  560. X            resetundo();
  561. X            addtobuff(Undobuff, 'i', gchar(Curschar), ESC, NUL);
  562. X            *Uncurschar = *Curschar;
  563. X            delchar(TRUE);
  564. X            updateline();
  565. X        }
  566. X        break;
  567. X
  568. X    case 'A':
  569. X        set_want_col = TRUE;
  570. X        while (oneright())
  571. X            ;
  572. X        /* fall through */
  573. X
  574. X    case 'a':
  575. X        CLEAROP;
  576. X        /* Works just like an 'i'nsert on the next character. */
  577. X        if (!lineempty())
  578. X            inc(Curschar);
  579. X        resetundo();
  580. X        startinsert(mkstr(c), FALSE);
  581. X        break;
  582. X
  583. X    case 'I':
  584. X        beginline(TRUE);
  585. X        /* fall through */
  586. X
  587. X    case 'i':
  588. X    case K_INSERT:
  589. X        CLEAROP;
  590. X        resetundo();
  591. X        startinsert(mkstr(c), FALSE);
  592. X        break;
  593. X
  594. X    case 'o':
  595. X        CLEAROP;
  596. X        opencmd(FORWARD, TRUE);
  597. X        resetundo();
  598. X        addtobuff(Undobuff, 'J', NULL);
  599. X        startinsert("o", TRUE);
  600. X        break;
  601. X
  602. X    case 'O':
  603. X        CLEAROP;
  604. X        opencmd(BACKWARD, TRUE);
  605. X        resetundo();
  606. X        startinsert("O", TRUE);
  607. X        break;
  608. X
  609. X    case 'd':
  610. X        if (operator == DELETE)        /* handle 'dd' */
  611. X            goto lineop;
  612. X        if (Prenum != 0)
  613. X            opnum = Prenum;
  614. X        startop = *Curschar;
  615. X        operator = DELETE;
  616. X        break;
  617. X
  618. X    /*
  619. X     * Some convenient abbreviations...
  620. X     */
  621. X
  622. X    case 'D':
  623. X        stuffin("d$");
  624. X        break;
  625. X
  626. X    case 'Y':
  627. X        if (Prenum)
  628. X            stuffnum(Prenum);
  629. X        stuffin("yy");
  630. X        break;
  631. X
  632. X    case 'C':
  633. X        stuffin("c$");
  634. X        break;
  635. X
  636. X    case 'c':
  637. X        if (operator == CHANGE) {    /* handle 'cc' */
  638. X            CLEAROP;
  639. X            stuffin("0c$");
  640. X            break;
  641. X        }
  642. X        if (Prenum != 0)
  643. X            opnum = Prenum;
  644. X        startop = *Curschar;
  645. X        operator = CHANGE;
  646. X        break;
  647. X
  648. X    case 'y':
  649. X        if (operator == YANK)        /* handle 'yy' */
  650. X            goto lineop;
  651. X        if (Prenum != 0)
  652. X            opnum = Prenum;
  653. X        startop = *Curschar;
  654. X        operator = YANK;
  655. X        break;
  656. X
  657. X    case 'p':
  658. X        doput(FORWARD);
  659. X        break;
  660. X
  661. X    case 'P':
  662. X        doput(BACKWARD);
  663. X        break;
  664. X
  665. X    case '>':
  666. X        if (operator == RSHIFT)        /* handle >> */
  667. X            goto lineop;
  668. X        if (Prenum != 0)
  669. X            opnum = Prenum;
  670. X        startop = *Curschar;
  671. X        operator = RSHIFT;
  672. X        break;
  673. X
  674. X    case '<':
  675. X        if (operator == LSHIFT)        /* handle << */
  676. X            goto lineop;
  677. X        if (Prenum != 0)
  678. X            opnum = Prenum;
  679. X        startop = *Curschar;    /* save current position */
  680. X        operator = LSHIFT;
  681. X        break;
  682. X
  683. X    case 's':                /* substitute characters */
  684. X        if (Prenum)
  685. X            stuffnum(Prenum);
  686. X        stuffin("c.");
  687. X        break;
  688. X
  689. X    case '?':
  690. X    case '/':
  691. X    case ':':
  692. X        CLEAROP;
  693. X        readcmdline(c, NULL);
  694. X        break;
  695. X
  696. X    case 'n':
  697. X        mtype = MCHAR;
  698. X        mincl = FALSE;
  699. X        set_want_col = TRUE;
  700. X        repsearch(0);
  701. X        break;
  702. X
  703. X    case 'N':
  704. X        mtype = MCHAR;
  705. X        mincl = FALSE;
  706. X        set_want_col = TRUE;
  707. X        repsearch(1);
  708. X        break;
  709. X
  710. X    /*
  711. X     * Character searches
  712. X     */
  713. X    case 'T':
  714. X        dir = BACKWARD;
  715. X        /* fall through */
  716. X
  717. X    case 't':
  718. X        type = 1;
  719. X        goto docsearch;
  720. X
  721. X    case 'F':
  722. X        dir = BACKWARD;
  723. X        /* fall through */
  724. X
  725. X    case 'f':
  726. X    docsearch:
  727. X        mtype = MCHAR;
  728. X        mincl = TRUE;
  729. X        set_want_col = TRUE;
  730. X        if ((nchar = vgetc()) == ESC)    /* search char */
  731. X            break;
  732. X        if (!searchc(nchar, dir, type))
  733. X            beep();
  734. X        break;
  735. X
  736. X    case ',':
  737. X        flag = 1;
  738. X        /* fall through */
  739. X
  740. X    case ';':
  741. X        mtype = MCHAR;
  742. X        mincl = TRUE;
  743. X        set_want_col = TRUE;
  744. X        if (!crepsearch(flag))
  745. X            beep();
  746. X        break;
  747. X
  748. X    /*
  749. X     * Function searches
  750. X     */
  751. X
  752. X    case '[':
  753. X        dir = BACKWARD;
  754. X        /* fall through */
  755. X
  756. X    case ']':
  757. X        mtype = MLINE;
  758. X        set_want_col = TRUE;
  759. X        if (vgetc() != c)
  760. X            beep();
  761. X
  762. X        if (!findfunc(dir))
  763. X            beep();
  764. X        break;
  765. X
  766. X    /*
  767. X     * Marks
  768. X     */
  769. X
  770. X    case 'm':
  771. X        CLEAROP;
  772. X        if (!setmark(vgetc()))
  773. X            beep();
  774. X        break;
  775. X
  776. X    case '\'':
  777. X        flag = TRUE;
  778. X        /* fall through */
  779. X
  780. X    case '`':
  781. X        {
  782. X            LPTR    mtmp, *mark = getmark(vgetc());
  783. X
  784. X            if (mark == NULL)
  785. X                beep();
  786. X            else {
  787. X                mtmp = *mark;
  788. X                setpcmark();
  789. X                *Curschar = mtmp;
  790. X                if (flag)
  791. X                    beginline(TRUE);
  792. X            }
  793. X            mtype = flag ? MLINE : MCHAR;
  794. X            mincl = TRUE;        /* ignored if not MCHAR */
  795. X            set_want_col = TRUE;
  796. X        }
  797. X        break;
  798. X
  799. X    case 'r':
  800. X        CLEAROP;
  801. X        if (lineempty()) {    /* Nothing to replace */
  802. X            beep();
  803. X            break;
  804. X        }
  805. X        if ((nchar = vgetc()) == ESC)
  806. X            break;
  807. X        resetundo();
  808. X
  809. X        addtobuff(Undobuff, 'r', gchar(Curschar), NULL);
  810. X        *Uncurschar = *Curschar;
  811. X
  812. X        /* Change current character. */
  813. X        pchar(Curschar, nchar);
  814. X
  815. X        /* Save stuff necessary to redo it */
  816. X        addtobuff(Redobuff, 'r', nchar, NULL);
  817. X
  818. X        CHANGED;
  819. X        can_undo = TRUE;
  820. X        updateline();
  821. X        break;
  822. X
  823. X    case '~':        /* swap case */
  824. X        CLEAROP;
  825. X        if (lineempty()) {
  826. X            beep();
  827. X            break;
  828. X        }
  829. X        c = gchar(Curschar);
  830. X
  831. X        if (isalpha(c)) {
  832. X            stuffin("r");        /* replace with other case */
  833. X            if (islower(c))
  834. X                stuffin(mkstr(toupper(c)));
  835. X            else
  836. X                stuffin(mkstr(tolower(c)));
  837. X        }
  838. X        stuffin("l");            /* move right when done */
  839. X
  840. X        break;
  841. X
  842. X    case 'J':
  843. X        CLEAROP;
  844. X
  845. X        if (!dojoin())
  846. X            beep();
  847. X
  848. X        resetundo();
  849. X        *Uncurschar = *Curschar;
  850. X        addtobuff(Undobuff, 'i', NL, ESC, NULL);
  851. X        addtobuff(Redobuff,'J',NULL);
  852. X        updatescreen();
  853. X        break;
  854. X
  855. X    case K_CGRAVE:            /* shorthand command */
  856. X        CLEAROP;
  857. X        stuffin(":e #\n");
  858. X        break;
  859. X
  860. X    case 'Z':            /* write, if changed, and exit */
  861. X        if (vgetc() != 'Z') {
  862. X            beep();
  863. X            break;
  864. X        }
  865. X
  866. X        if (Changed) {
  867. X            if (Filename != NULL) {
  868. X                if (!writeit(Filename, NULL, NULL))
  869. X                    return;
  870. X            } else {
  871. X                emsg("No output file");
  872. X                return;
  873. X            }
  874. X        }
  875. X        getout();
  876. X        break;
  877. X
  878. X    case '.':
  879. X        /*
  880. X         * If a delete is in effect, we let '.' help out the same
  881. X         * way that '_' helps for some line operations. It's like
  882. X         * an 'l', but subtracts one from the count and is inclusive.
  883. X         */
  884. X        if (operator == DELETE || operator == CHANGE) {
  885. X            if (Prenum != 0) {
  886. X                n = DEFAULT1(Prenum) - 1;
  887. X                while (n--)
  888. X                    if (! oneright())
  889. X                        break;
  890. X            }
  891. X            mtype = MCHAR;
  892. X            mincl = TRUE;
  893. X        } else {            /* a normal 'redo' */
  894. X            CLEAROP;
  895. X            stuffin(Redobuff);
  896. X        }
  897. X        break;
  898. X
  899. X    case 'u':
  900. X    case K_UNDO:
  901. X        CLEAROP;
  902. X        if (!can_undo) {
  903. X            msg("Sorry, can't undo last edit");
  904. X            break;
  905. X        }
  906. X
  907. X        if ( *Undobuff != NUL ) {
  908. X            *Curschar = *Uncurschar;
  909. X            stuffin(Undobuff);
  910. X            *Undobuff = NUL;
  911. X        }
  912. X        if ( Undelchars > 0 ) {
  913. X            *Curschar = *Uncurschar;
  914. X            /* construct the next Undobuff and Redobuff, which */
  915. X            /* will re-insert the characters we're deleting. */
  916. X            p = Undobuff;
  917. X            q = Redobuff;
  918. X            *p++ = *q++ = 'i';
  919. X            /*
  920. X             * Fix this loop to effectively turn nulls into
  921. X             * NL's in the Undo and Redo buffs and do the
  922. X             * joins needed.
  923. X             */
  924. X            while ( Undelchars-- > 0 ) {
  925. X                char    c = gchar(Curschar);
  926. X
  927. X                if (c == NUL) {
  928. X                    *p++ = *q++ = NL;
  929. X                    dojoin();
  930. X                } else {
  931. X                    *p++ = *q++ = c;
  932. X                    delchar(FALSE);
  933. X                }
  934. X            }
  935. X            /* Finish constructing Uncursbuff, and Uncurschar */
  936. X            /* is left unchanged. */
  937. X            *p++ = *q++ = ESC;
  938. X            *p = *q = NUL;
  939. X            /* Undelchars has been reset to 0 */
  940. X            updatescreen();
  941. X        }
  942. X        can_undo = FALSE;
  943. X        break;
  944. X
  945. X    default:
  946. X        CLEAROP;
  947. X        beep();
  948. X        break;
  949. X    }
  950. X
  951. X    /*
  952. X     * If an operation is pending, handle it...
  953. X     */
  954. X    if (finish_op) {        /* we just finished an operator */
  955. X        if (operator == NOP)    /* ... but it was cancelled */
  956. X            return;
  957. X
  958. X        switch (operator) {
  959. X
  960. X        case LSHIFT:
  961. X        case RSHIFT:
  962. X            doshift(operator, c, nchar, Prenum);
  963. X            break;
  964. X
  965. X        case DELETE:
  966. X            dodelete(c, nchar, Prenum);
  967. X            break;
  968. X
  969. X        case YANK:
  970. X            doyank();    /* no redo on yank... */
  971. X            break;
  972. X
  973. X        case CHANGE:
  974. X            dochange(c, nchar, Prenum);
  975. X            break;
  976. X
  977. X        default:
  978. X            beep();
  979. X        }
  980. X        operator = NOP;
  981. X    }
  982. X}
  983. X
  984. X/*
  985. X * doshift - handle a shift operation
  986. X */
  987. static void
  988. doshift(op, c1, c2, num)
  989. int    op;
  990. char    c1, c2;
  991. int    num;
  992. X{
  993. X    LPTR    top, bot;
  994. X    int    nlines;
  995. X    char    opchar;
  996. X
  997. X    top = startop;
  998. X    bot = *Curschar;
  999. X
  1000. X    if (lt(&bot, &top))
  1001. X        pswap(&top, &bot);
  1002. X
  1003. X    nlines = cntllines(&top, &bot);
  1004. X    *Curschar = top;
  1005. X    tabinout((op == LSHIFT), nlines);
  1006. X
  1007. X    /* construct Redo buff */
  1008. X    opchar = (op == LSHIFT) ? '<' : '>';
  1009. X    if (num != 0)
  1010. X        sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2);
  1011. X    else
  1012. X        sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
  1013. X
  1014. X    /*
  1015. X     * The cursor position afterward is the prior of the two positions.
  1016. X     */
  1017. X    *Curschar = top;
  1018. X
  1019. X    /*
  1020. X     * If we were on the last char of a line that got shifted left,
  1021. X     * then move left one so we aren't beyond the end of the line
  1022. X     */
  1023. X    if (gchar(Curschar) == NUL && Curschar->index > 0)
  1024. X        Curschar->index--;
  1025. X
  1026. X    updatescreen();
  1027. X
  1028. X    if (nlines > P(P_RP))
  1029. X        smsg("%d lines %ced", nlines, opchar);
  1030. X}
  1031. X
  1032. X/*
  1033. X * dodelete - handle a delete operation
  1034. X */
  1035. static void
  1036. dodelete(c1, c2, num)
  1037. char    c1, c2;
  1038. int    num;
  1039. X{
  1040. X    LPTR    top, bot;
  1041. X    int    nlines;
  1042. X    int    n;
  1043. X
  1044. X    /*
  1045. X     * Do a yank of whatever we're about to delete. If there's too much
  1046. X     * stuff to fit in the yank buffer, then get a confirmation before
  1047. X     * doing the delete. This is crude, but simple. And it avoids doing
  1048. X     * a delete of something we can't put back if we want.
  1049. X     */
  1050. X    if (!doyank()) {
  1051. X        msg("yank buffer exceeded: press <y> to confirm");
  1052. X        if (vgetc() != 'y') {
  1053. X            msg("delete aborted");
  1054. X            *Curschar = startop;
  1055. X            return;
  1056. X        }
  1057. X    }
  1058. X
  1059. X    top = startop;
  1060. X    bot = *Curschar;
  1061. X
  1062. X    if (lt(&bot, &top))
  1063. X        pswap(&top, &bot);
  1064. X
  1065. X    nlines = cntllines(&top, &bot);
  1066. X    *Curschar = top;
  1067. X    cursupdate();
  1068. X
  1069. X    if (mtype == MLINE) {
  1070. X        delline(nlines);
  1071. X    } else {
  1072. X        if (!mincl && bot.index != 0)
  1073. X            dec(&bot);
  1074. X
  1075. X        if (top.linep == bot.linep) {        /* del. within line */
  1076. X            n = bot.index - top.index + 1;
  1077. X            while (n--)
  1078. X                if (!delchar(TRUE))
  1079. X                    break;
  1080. X        } else {                /* del. between lines */
  1081. X            n = Curschar->index;
  1082. X            while (Curschar->index >= n)
  1083. X                if (!delchar(TRUE))
  1084. X                    break;
  1085. X
  1086. X            top = *Curschar;
  1087. X            *Curschar = *nextline(Curschar);
  1088. X            delline(nlines-2);
  1089. X            Curschar->index = 0;
  1090. X            n = bot.index + 1;
  1091. X            while (n--)
  1092. X                if (!delchar(TRUE))
  1093. X                    break;
  1094. X            *Curschar = top;
  1095. X            dojoin();
  1096. X        }
  1097. X    }
  1098. X
  1099. X    /* construct Redo buff */
  1100. X    if (num != 0)
  1101. X        sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  1102. X    else
  1103. X        sprintf(Redobuff, "d%c%c", c1, c2);
  1104. X
  1105. X    if (mtype == MCHAR && nlines == 1)
  1106. X        updateline();
  1107. X    else
  1108. X        updatescreen();
  1109. X
  1110. X    if (nlines > P(P_RP))
  1111. X        smsg("%d fewer lines", nlines);
  1112. X}
  1113. X
  1114. X/*
  1115. X * dochange - handle a change operation
  1116. X */
  1117. static void
  1118. dochange(c1, c2, num)
  1119. char    c1, c2;
  1120. int    num;
  1121. X{
  1122. X    char    sbuf[16];
  1123. X    bool_t    doappend;    /* true if we should do append, not insert */
  1124. X
  1125. X    doappend = endofline( (lt(Curschar, &startop)) ? &startop: Curschar);
  1126. X
  1127. X    if (mtype == MLINE) {
  1128. X        msg("multi-line changes not yet supported");
  1129. X        return;
  1130. X    }
  1131. X
  1132. X    dodelete(c1, c2, num);
  1133. X
  1134. X    if (num)
  1135. X        sprintf(sbuf, "c%d%c%c", num, c1, c2);
  1136. X    else
  1137. X        sprintf(sbuf, "c%c%c", c1, c2);
  1138. X
  1139. X    if (doappend && !lineempty())
  1140. X        inc(Curschar);
  1141. X
  1142. X    startinsert(sbuf);
  1143. X}
  1144. X
  1145. X#define    YBSIZE    1024
  1146. X
  1147. static    char    ybuf[YBSIZE];
  1148. static    int    ybtype = MBAD;
  1149. X
  1150. static bool_t
  1151. doyank()
  1152. X{
  1153. X    LPTR    top, bot;
  1154. X    char    *yptr = ybuf;
  1155. X    char    *ybend = &ybuf[YBSIZE-1];
  1156. X    int    nlines;
  1157. X
  1158. X    top = startop;
  1159. X    bot = *Curschar;
  1160. X
  1161. X    if (lt(&bot, &top))
  1162. X        pswap(&top, &bot);
  1163. X
  1164. X    nlines = cntllines(&top, &bot);
  1165. X
  1166. X    ybtype = mtype;            /* set the yank buffer type */
  1167. X
  1168. X    if (mtype == MLINE) {
  1169. X        top.index = 0;
  1170. X        bot.index = strlen(bot.linep->s);
  1171. X        /*
  1172. X         * The following statement checks for the special case of
  1173. X         * yanking a blank line at the beginning of the file. If
  1174. X         * not handled right, we yank an extra char (a newline).
  1175. X         */
  1176. X        if (dec(&bot) == -1) {
  1177. X            ybuf[0] = NUL;
  1178. X            if (operator == YANK)
  1179. X                *Curschar = startop;
  1180. X            return TRUE;
  1181. X        }
  1182. X    } else {
  1183. X        if (!mincl) {
  1184. X            if (bot.index)
  1185. X                bot.index--;
  1186. X        }
  1187. X    }
  1188. X
  1189. X    for (; ltoreq(&top, &bot) ;inc(&top)) {
  1190. X        *yptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  1191. X        if (++yptr >= ybend) {
  1192. X            msg("yank too big for buffer");
  1193. X            ybtype = MBAD;
  1194. X            return FALSE;
  1195. X        }
  1196. X    }
  1197. X
  1198. X    *yptr = NUL;
  1199. X
  1200. X    if (operator == YANK) {    /* restore Curschar if really doing yank */
  1201. X        *Curschar = startop;
  1202. X
  1203. X        if (nlines > P(P_RP))
  1204. X            smsg("%d lines yanked", nlines);
  1205. X    }
  1206. X
  1207. X    return TRUE;
  1208. X}
  1209. X
  1210. static void
  1211. doput(dir)
  1212. int    dir;
  1213. X{
  1214. X    if (ybtype == MBAD) {
  1215. X        beep();
  1216. X        return;
  1217. X    }
  1218. X    
  1219. X    if (dir == FORWARD)
  1220. X        stuffin( (ybtype == MCHAR) ? "a" : "o" );
  1221. X    else
  1222. X        stuffin( (ybtype == MCHAR) ? "i" : "O" );
  1223. X
  1224. X    stuffin(ybuf);
  1225. X    stuffin(mkstr(ESC));
  1226. X}
  1227. X
  1228. X/*
  1229. X * tabinout(inout,num)
  1230. X *
  1231. X * If inout==0, add a tab to the begining of the next num lines.
  1232. X * If inout==1, delete a tab from the beginning of the next num lines.
  1233. X */
  1234. static void
  1235. tabinout(inout, num)
  1236. int    inout;
  1237. int    num;
  1238. X{
  1239. X    int    ntodo = num;
  1240. X    LPTR    *p;
  1241. X
  1242. X    /* construct undo stuff */
  1243. X    resetundo();
  1244. X    *Uncurschar = *Curschar;
  1245. X    sprintf(Undobuff, "%d%s", num, (inout == 0) ? "<<" : ">>");
  1246. X
  1247. X    beginline(FALSE);
  1248. X    while ( ntodo-- > 0 ) {
  1249. X        beginline(FALSE);
  1250. X        if ( inout == 0 )
  1251. X            inschar(TAB);
  1252. X        else {
  1253. X            if ( gchar(Curschar) == TAB )
  1254. X                delchar(TRUE);
  1255. X        }
  1256. X        if ( ntodo > 0 ) {
  1257. X            if ( (p=nextline(Curschar)) != NULL )
  1258. X                *Curschar = *p;
  1259. X            else
  1260. X                break;
  1261. X        }
  1262. X    }
  1263. X    can_undo = TRUE;
  1264. X}
  1265. X
  1266. static void
  1267. startinsert(initstr, startln)
  1268. char *initstr;
  1269. int    startln;    /* if set, insert point really at start of line */
  1270. X{
  1271. X    char *p, c;
  1272. X
  1273. X    *Insstart = *Curschar;
  1274. X    if (startln)
  1275. X        Insstart->index = 0;
  1276. X    Ninsert = 0;
  1277. X    Insptr = Insbuff;
  1278. X    for (p=initstr; (c=(*p++))!='\0'; )
  1279. X        *Insptr++ = c;
  1280. X    State = INSERT;
  1281. X    if (P(P_MO))
  1282. X        msg("Insert Mode");
  1283. X}
  1284. X
  1285. void
  1286. resetundo()
  1287. X{
  1288. X    Undelchars = 0;
  1289. X    *Undobuff = '\0';
  1290. X    Uncurschar->linep = NULL;
  1291. X}
  1292. X
  1293. static bool_t
  1294. dojoin()
  1295. X{
  1296. X    int    scol;        /* save cursor column */
  1297. X    int    size;        /* size of the joined line */
  1298. X
  1299. X    if (nextline(Curschar) == NULL)        /* on last line */
  1300. X        return FALSE;
  1301. X
  1302. X    if (!canincrease(size = strlen(Curschar->linep->next->s)))
  1303. X        return FALSE;
  1304. X
  1305. X    while (oneright())            /* to end of line */
  1306. X        ;
  1307. X
  1308. X    strcat(Curschar->linep->s, Curschar->linep->next->s);
  1309. X
  1310. X    /*
  1311. X     * Delete the following line. To do this we move the cursor
  1312. X     * there briefly, and then move it back. Don't back up if the
  1313. X     * delete made us the last line.
  1314. X     */
  1315. X    Curschar->linep = Curschar->linep->next;
  1316. X    scol = Curschar->index;
  1317. X
  1318. X    if (nextline(Curschar) != NULL) {
  1319. X        delline(1);
  1320. X        Curschar->linep = Curschar->linep->prev;
  1321. X    } else
  1322. X        delline(1);
  1323. X
  1324. X    Curschar->index = scol;
  1325. X
  1326. X    oneright();        /* go to first char. of joined line */
  1327. X
  1328. X    if (size != 0) {
  1329. X        /*
  1330. X         * Delete leading white space on the joined line
  1331. X         * and insert a single space.
  1332. X         */
  1333. X        while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB)
  1334. X            delchar(TRUE);
  1335. X        inschar(' ');
  1336. X    }
  1337. X
  1338. X    return TRUE;
  1339. X}
  1340. X
  1341. char *
  1342. mkstr(c)
  1343. char    c;
  1344. X{
  1345. X    static    char    s[2];
  1346. X
  1347. X    s[0] = c;
  1348. X    s[1] = NUL;
  1349. X
  1350. X    return s;
  1351. X}
  1352. END_OF_FILE
  1353. if test 23691 -ne `wc -c <'normal.c'`; then
  1354.     echo shar: \"'normal.c'\" unpacked with wrong size!
  1355. fi
  1356. # end of 'normal.c'
  1357. fi
  1358. echo shar: End of archive 4 \(of 4\).
  1359. cp /dev/null ark4isdone
  1360. MISSING=""
  1361. for I in 1 2 3 4 ; do
  1362.     if test ! -f ark${I}isdone ; then
  1363.     MISSING="${MISSING} ${I}"
  1364.     fi
  1365. done
  1366. if test "${MISSING}" = "" ; then
  1367.     echo You have unpacked all 4 archives.
  1368.     rm -f ark[1-9]isdone
  1369. else
  1370.     echo You still need to unpack the following archives:
  1371.     echo "        " ${MISSING}
  1372. fi
  1373. ##  End of shell archive.
  1374. exit 0
  1375.